# frozen_string_literal: true

module EE
  module Gitlab
    module BackgroundMigration
      module MigrateSharedVulnerabilityIdentifiers
        extend ActiveSupport::Concern
        extend ::Gitlab::Utils::Override

        REPORT_TYPES = { cluster_image_scanning: 7, generic: 99 }.freeze

        class Finding < ::ApplicationRecord # rubocop:disable Style/Documentation
          include ::EachBatch

          self.table_name = "vulnerability_occurrences"

          # pipeline fails otherwise
          validates :details, json_schema: { filename: "vulnerability_finding_details" }, if: false

          has_many :finding_identifiers, inverse_of: :finding, foreign_key: "occurrence_id"
          has_many :identifiers, through: :finding_identifiers

          def self.to_process
            joins(:identifiers)
              .where("vulnerability_occurrences.project_id != vulnerability_identifiers.project_id")
              .distinct
          end
        end

        class FindingIdentifier < ::ApplicationRecord # rubocop:disable Style/Documentation
          self.table_name = "vulnerability_occurrence_identifiers"

          belongs_to :finding, inverse_of: :finding_identifiers, foreign_key: "occurrence_id"
          belongs_to :identifier, inverse_of: :finding_identifiers
        end

        class Identifier < ::ApplicationRecord # rubocop:disable Style/Documentation
          include ::EachBatch

          self.table_name = "vulnerability_identifiers"

          has_many :finding_identifiers, inverse_of: :identifier
          has_many :findings, through: :finding_identifiers

          def self.find_or_create_id_for(project_id, identifier)
            current_time = Time.zone.now
            attrs = identifier.attributes.except("id").merge(project_id: project_id,
              created_at: current_time,
              updated_at: current_time)
            result = upsert(attrs, unique_by: :index_vulnerability_identifiers_on_project_id_and_fingerprint)
            result.rows.first.first
          end
        end

        class VulnerabilityRead < ::ApplicationRecord # rubocop:disable Style/Documentation
          self.table_name = "vulnerability_reads"
        end

        prepended do
          operation_name :migrate_shared_vulnerability_identifiers

          scope_to ->(relation) do
            return Finding.none if ::Gitlab.com?

            relation.where(report_type: REPORT_TYPES.values)
          end
        end

        override :perform
        def perform
          each_sub_batch(batching_scope: ->(relation) { Finding.to_process.merge(relation) }) do |batch|
            batch.each do |finding|
              process(finding)
            end
          end
        end

        private

        def process(finding)
          finding.identifiers.each do |identifier|
            correct_id = Identifier.find_or_create_id_for(finding.project_id, identifier)

            FindingIdentifier
              .where(occurrence_id: finding.id, identifier_id: identifier.id)
              .update_all(identifier_id: correct_id)
          end
        end
      end
    end
  end
end
